use serde::{Deserialize, Serialize};
use std::net::IpAddr;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AclConfig {
    #[serde(default)]
    pub rules: Vec<AclRule>,
    #[serde(default)]
    pub default_action: AclAction,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AclRule {
    #[serde(rename = "type")]
    pub rule_type: AclRuleType,
    pub target: String,
    pub action: AclAction,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AclRuleType {
    Domain,
    Ip,
    Cidr,
    GeoIp,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AclAction {
    Allow,
    Deny,
    Direct,
    Proxy,
}

impl Default for AclConfig {
    fn default() -> Self {
        Self {
            rules: Vec::new(),
            default_action: AclAction::Proxy,
        }
    }
}

impl Default for AclAction {
    fn default() -> Self {
        AclAction::Proxy
    }
}

pub struct AclMatcher {
    config: AclConfig,
}

impl AclMatcher {
    pub fn new(config: AclConfig) -> Self {
        Self { config }
    }

    pub fn check(&self, target: &str) -> AclAction {
        // Check each rule
        for rule in &self.config.rules {
            if self.matches_rule(rule, target) {
                return rule.action.clone();
            }
        }

        // Return default action if no rule matches
        self.config.default_action.clone()
    }

    fn matches_rule(&self, rule: &AclRule, target: &str) -> bool {
        match rule.rule_type {
            AclRuleType::Domain => {
                // Simple domain matching
                target.contains(&rule.target)
            }
            AclRuleType::Ip => {
                // IP address matching
                if let Ok(ip) = target.parse::<IpAddr>() {
                    if let Ok(rule_ip) = rule.target.parse::<IpAddr>() {
                        return ip == rule_ip;
                    }
                }
                false
            }
            AclRuleType::Cidr => {
                // CIDR matching (simplified)
                false
            }
            AclRuleType::GeoIp => {
                // GeoIP matching (requires external database)
                false
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_acl_domain_match() {
        let config = AclConfig {
            rules: vec![AclRule {
                rule_type: AclRuleType::Domain,
                target: "google.com".to_string(),
                action: AclAction::Direct,
            }],
            default_action: AclAction::Proxy,
        };

        let matcher = AclMatcher::new(config);
        assert!(matches!(matcher.check("www.google.com"), AclAction::Direct));
        assert!(matches!(matcher.check("www.example.com"), AclAction::Proxy));
    }
}
